package com.geek.bulk.interceptor;

import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Objects;


public abstract class AnnotatedInterceptor<A extends Annotation> {

    /**
     * 子类的注解
     */
    private final Class<A> bindingAnnotationType;

    public AnnotatedInterceptor() {
        this.bindingAnnotationType = resolveBindingAnnotationType();
    }


    @AroundInvoke
    public final Object execute(InvocationContext context) throws Exception {
        //找到拦截类上要拦截的注解
        A interceptorBindingAnnotation = findInterceptorBindingAnnotation(context.getMethod());
        if (interceptorBindingAnnotation == null) {
            return context.proceed();
        }
        return execute(context, interceptorBindingAnnotation);
    }

    protected abstract Object execute(InvocationContext context, A interceptorBindingAnnotation) throws Exception;

    /**
     * 将拦截的方法上的注解获取到
     * @param method
     * @return
     */
    private A findInterceptorBindingAnnotation(Method method) {
        Annotation[] declaredAnnotations = method.getDeclaredAnnotations();
        for (Annotation declaredAnnotation : declaredAnnotations) {
            if (Objects.equals(declaredAnnotation.annotationType(), bindingAnnotationType)) {
                return (A) declaredAnnotation;
            }
        }
        return null;
    }


    /**
     * 目的: 获取实现类的泛型类
     * @see com.geek.domain.AnnotationReflectDomain
     * @return
     */
    private Class<A> resolveBindingAnnotationType() {
        Type type = getClass().getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            //注解类: <A>
            for (Type typeArgument : parameterizedType.getActualTypeArguments()) {
                if (typeArgument instanceof Class) {
                    return (Class) typeArgument;
                }
            }
        }
        return null;
    }

}
